package com.clps.myssm.myspringmvc;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import com.clps.myssm.ioc.BeanFactory;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.math.BigDecimal;
import java.util.stream.Collectors;

/**
 * @author: Chase
 * @date: 2024/8/29 21:31
 * @Version: 1.0
 * @description:
 */

@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {

    private BeanFactory beanFactory ;

    public DispatcherServlet(){
    }

    @Override
    public void init() throws ServletException {
        super.init();
        //之前是在此处主动创建IOC容器的
        //beanFactory = new ClassPathXmlApplicationContext();
        //现在优化为从application作用域去获取
        ServletContext application = getServletContext();
        Object beanFactoryObj = application.getAttribute("beanFactory");
        if(beanFactoryObj!=null){
            beanFactory = (BeanFactory)beanFactoryObj ;
        }else{
            throw new RuntimeException("IOC容器获取失败！");
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*
            设置编码
            request.setCharacterEncoding("UTF-8");
            假设url是：  http://localhost:8080/pro15/hello.do
            那么servletPath是：    /hello.do
             我的思路是：
             第1步： /hello.do ->   hello
             第2步： hello -> HelloController
         */
        String servletPath = request.getServletPath();
        servletPath = servletPath.substring(1);
        int lastDoIndex = servletPath.lastIndexOf(".do") ;
        servletPath = servletPath.substring(0,lastDoIndex);

        Object controllerBeanObj = beanFactory.getBean(servletPath);

        String operate = request.getParameter("operate");
        if(StrUtil.isEmpty(operate)){
            throw new RuntimeException("operate不能为空！");
        }

        try {
            Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
            for(Method method : methods){
                if(operate.equals(method.getName())){
                    //1.统一获取请求参数

                    //1-1.获取当前方法的参数，返回参数数组
                    Parameter[] parameters = method.getParameters();
                    //1-2.parameterValues 用来承载参数的值
                    Object[] parameterValues = new Object[parameters.length];
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];
                        String parameterName = parameter.getName() ;
                        String typeName = parameter.getType().getName();
                        //如果参数名是request,response,session 那么就不是通过请求中获取参数的方式了
                        if("request".equals(parameterName)){
                            parameterValues[i] = request ;
                        }else if("response".equals(parameterName)){
                            parameterValues[i] = response ;
                        }else if("session".equals(parameterName)){
                            parameterValues[i] = request.getSession() ;
                        }else if (isContained(typeName)){
                            String objJson = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
                            if (StrUtil.isNotEmpty(objJson)) {
                                Object parameterObj = JSONUtil.toBean(objJson, Class.forName(typeName));
                                parameterValues[i] = parameterObj ;
                            }
                        }else{
                            //从请求中获取参数值
                            String parameterValue = request.getParameter(parameterName);
                            Object parameterObj = parameterValue ;

                            if(parameterObj!=null) {
                                // 此处要转换，常见错误： IllegalArgumentException: argument type mismatch
                                if ("java.lang.Integer".equals(typeName)) {
                                    parameterObj = Integer.parseInt(parameterValue);
                                } else if ("java.lang.Long".equals(typeName)) {
                                    parameterObj = Long.parseLong(parameterValue);
                                } else if ("java.math.BigDecimal".equals(typeName)) {
                                    parameterObj = new BigDecimal(parameterValue);
                                }
                            }

                            parameterValues[i] = parameterObj ;
                        }
                    }
                    //2.controller组件中的方法调用
                    method.setAccessible(true);
                    Object returnObj = method.invoke(controllerBeanObj,parameterValues);

                    //3.返回数据处理 (将对象转换成json数据)
                    if (ObjectUtil.isEmpty(returnObj)) {
                        return ;
                    } else {
                        response.setCharacterEncoding("utf-8");
                        response.setContentType("application/json;charset=utf-8");
                        String jsonStr = JSONUtil.toJsonStr(returnObj, getJsonConfig());
                        PrintWriter out = response.getWriter();
                        out.print(jsonStr);
                        out.flush();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new DispatcherServletException("DispatcherServlet出错了...");
        }
    }

    private JSONConfig getJsonConfig() {
        // 创建JSONConfig对象，并设置日期格式
        JSONConfig jsonConfig = JSONConfig.create();
        jsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
        jsonConfig.setStripTrailingZeros(false);
        return jsonConfig;
    }

    private Boolean isContained(String className) {
        if (StrUtil.isEmpty(className)) {
            return false;
        }
        return className.contains("com.clps.digital.model");
    }
}